/*
 * UAE - The Un*x Amiga Emulator
 *
 * Gayle (and motherboard resources) memory bank
 *
 * (c) 2006 - 2011 Toni Wilen
 */

#include "sysconfig.h"
#include "ncr_scsi.h"
#include "options.h"
#include "savestate.h"
#include "uae.h"
#include "gui.h"
#include "newcpu.h"
#include "custom.h"
#include "filesys.h"
#include "hardfile.h"
#include "gayle.h"

/*
   600000 to 9FFFFF	4 MB	Credit Card memory if CC present
   A00000 to A1FFFF	128 KB	Credit Card Attributes
   A20000 to A3FFFF	128 KB	Credit Card I/O
   A40000 to A5FFFF	128 KB	Credit Card Bits
   A60000 to A7FFFF	128 KB	PC I/O

   D80000 to D8FFFF	64 KB SPARE chip select
   D90000 to D9FFFF	64 KB ARCNET chip select
   DA0000 to DA3FFF	16 KB IDE drive
   DA4000 to DA4FFF	16 KB IDE reserved
   DA8000 to DAFFFF	32 KB Credit Card and IDE configregisters
   DB0000 to DBFFFF	64 KB Not used (reserved for external IDE)
 * DC0000 to DCFFFF	64 KB Real Time Clock (RTC)
   DD0000 to DDFFFF	64 KB A3000 DMA controller
   DD0000 to DD1FFF        A4000 DMAC
   DD2000 to DDFFFF        A4000 IDE
   DE0000 to DEFFFF	64 KB Motherboard resources
 */

#define NCR_OFFSET 0x40

/* Gayle definitions from Linux drivers and preliminary Gayle datasheet */

/* PCMCIA stuff */

#define GAYLE_RAM 0x600000
#define GAYLE_RAMSIZE 0x400000
#define GAYLE_ATTRIBUTE 0xa00000
#define GAYLE_ATTRIBUTESIZE 0x020000
#define GAYLE_IO 0xa20000                    /* 16bit and even 8bit registers */
#define GAYLE_IOSIZE 0x010000
#define GAYLE_IO_8BITODD 0xa30000            /* odd 8bit registers */

#define GAYLE_ADDRESS 0xda8000        /* gayle main registers base address */
#define GAYLE_RESET 0xa40000          /* write 0x00 to start reset,
                                         read 1 byte to stop reset */
/* IDE stuff */

/*  Bases of the IDE interfaces */
#define GAYLE_BASE_4000 0xdd2020    /* A4000/A4000T */
#define GAYLE_BASE_1200 0xda0000    /* A1200/A600 and E-Matrix 530 */
/* IDE drive registers */
#define IDE_DATA 0x00
#define IDE_ERROR 0x01          /* see err-bits */
#define IDE_NSECTOR 0x02        /* nr of sectors to read/write */
#define IDE_SECTOR 0x03         /* starting sector */
#define IDE_LCYL 0x04           /* starting cylinder */
#define IDE_HCYL 0x05           /* high byte of starting cyl */
#define IDE_SELECT 0x06         /* 101dhhhh , d=drive, hhhh=head */
#define IDE_STATUS 0x07         /* see status-bits */
#define IDE_DEVCON 0x0406
#define IDE_DRVADDR 0x0407
/* STATUS bits */
#define IDE_STATUS_ERR 0x01
#define IDE_STATUS_IDX 0x02
#define IDE_STATUS_DRQ 0x08
#define IDE_STATUS_DSC 0x10
#define IDE_STATUS_DRDY 0x40
#define IDE_STATUS_BSY 0x80
/* ERROR bits */
#define IDE_ERR_UNC 0x40
#define IDE_ERR_MC 0x20
#define IDE_ERR_IDNF 0x10
#define IDE_ERR_MCR 0x08
#define IDE_ERR_ABRT 0x04
#define IDE_ERR_NM 0x02

/*
 *  These are at different offsets from the base
 */
#define GAYLE_IRQ_4000 0x3020     /* MSB = 1, Harddisk is source of interrupt */
#define GAYLE_CS_1200 0x8000
#define GAYLE_IRQ_1200 0x9000
#define GAYLE_INT_1200 0xA000
#define GAYLE_CFG_1200 0xB000

/* DA8000 */
#define GAYLE_CS_IDE 0x80       /* IDE int status */
#define GAYLE_CS_CCDET 0x40     /* credit card detect */
#define GAYLE_CS_BVD1 0x20      /* battery voltage detect 1 */
#define GAYLE_CS_SC 0x20    /* credit card status change */
#define GAYLE_CS_BVD2 0x10      /* battery voltage detect 2 */
#define GAYLE_CS_DA 0x10    /* digital audio */
#define GAYLE_CS_WR 0x08    /* write enable (1 == enabled) */
#define GAYLE_CS_BSY 0x04       /* credit card busy */
#define GAYLE_CS_IRQ 0x04       /* interrupt request */
#define GAYLE_CS_DAEN 0x02      /* enable digital audio */
#define GAYLE_CS_DIS 0x01       /* disable PCMCIA slot */

/* DA9000 */
#define GAYLE_IRQ_IDE 0x80
#define GAYLE_IRQ_CCDET 0x40        /* credit card detect */
#define GAYLE_IRQ_BVD1 0x20         /* battery voltage detect 1 */
#define GAYLE_IRQ_SC 0x20           /* credit card status change */
#define GAYLE_IRQ_BVD2 0x10         /* battery voltage detect 2 */
#define GAYLE_IRQ_DA 0x10           /* digital audio */
#define GAYLE_IRQ_WR 0x08           /* write enable (1 == enabled) */
#define GAYLE_IRQ_BSY 0x04          /* credit card busy */
#define GAYLE_IRQ_IRQ 0x04          /* interrupt request */
#define GAYLE_IRQ_RESET 0x02        /* reset machine after CCDET change */
#define GAYLE_IRQ_BERR 0x01         /* generate bus error after CCDET change */

/* DAA000 */
#define GAYLE_INT_IDE 0x80          /* IDE interrupt enable */
#define GAYLE_INT_CCDET 0x40        /* credit card detect change enable */
#define GAYLE_INT_BVD1 0x20         /* battery voltage detect 1 change enable */
#define GAYLE_INT_SC 0x20           /* credit card status change enable */
#define GAYLE_INT_BVD2 0x10         /* battery voltage detect 2 change enable */
#define GAYLE_INT_DA 0x10           /* digital audio change enable */
#define GAYLE_INT_WR 0x08           /* write enable change enabled */
#define GAYLE_INT_BSY 0x04          /* credit card busy */
#define GAYLE_INT_IRQ 0x04          /* credit card interrupt request */
#define GAYLE_INT_BVD_LEV 0x02      /* BVD int level, 0=lev2,1=lev6 */
#define GAYLE_INT_BSY_LEV 0x01      /* BSY int level, 0=lev2,1=lev6 */

/* 0xDAB000 GAYLE_CONFIG */
#define GAYLE_CFG_0V 0x00
#define GAYLE_CFG_5V 0x01
#define GAYLE_CFG_12V 0x02
#define GAYLE_CFG_100NS 0x08
#define GAYLE_CFG_150NS 0x04
#define GAYLE_CFG_250NS 0x00
#define GAYLE_CFG_720NS 0x0c

#define IDE_GAYLE 0
#define IDE_ADIDE 1

#define MAX_IDE_MULTIPLE_SECTORS 64
#define SECBUF_SIZE (512 * (MAX_IDE_MULTIPLE_SECTORS * 2))

struct ide_hdf
{
    struct hd_hardfiledata hdhfd;

    byte secbuf[SECBUF_SIZE];
    int data_offset;
    int data_size;
    int data_multi;
    int lba48;
    byte multiple_mode;
    byte status;
    int irq_delay;
    int irq;
    int num;
    int type;
    int blocksize;
    int maxtransferstate;

    void clear()
    {
        memset(this, 0, sizeof(ide_hdf));
    }
};

static struct ide_hdf* idedrive[4];
struct hd_hardfiledata* pcmcia_sram;
static int pcmcia_card;
static int pcmcia_readonly;

static int gayle_id_cnt;
static byte gayle_irq, gayle_int, gayle_cs, gayle_cs_mask, gayle_cfg;
static byte ide_select, ide_nsector, ide_sector, ide_lcyl, ide_hcyl, ide_devcon, ide_error, ide_feat;
static byte ide_nsector2, ide_sector2, ide_lcyl2, ide_hcyl2, ide_feat2;
static int ide_drv, ide2, ide_splitter;

static struct ide_hdf* ide;

static __forceinline void pw(int offset, ushort w)
{
    ide->secbuf[offset * 2 + 0] = (byte)w;
    ide->secbuf[offset * 2 + 1] = w >> 8;
}
static void ps(int offset, const TCHAR* src, int max)
{
    int i, len;
    char* s;

    offset *= 2;
    s = Unicode::ua(src);
    len = strlen(s);
    for (i = 0; i < max; i++)
    {
        char c = ' ';
        if (i < len)
            c = s[i];
        ide->secbuf[offset ^ 1] = c;
        offset++;
    }
    free(s);
}

static int isideirq()
{
    if (!idedrive)
        return 0;
    if (ide_devcon & 2)
        return 0;
    if (idedrive[ide_drv] == nullptr)
        return 0;
    if (idedrive[ide_drv]->irq || idedrive[ide_drv + 2]->irq)
    {
        /* IDE killer feature. Do not eat interrupt to make booting faster. */
        if (idedrive[ide_drv]->irq && idedrive[ide_drv]->hdhfd.size == 0)
            idedrive[ide_drv]->irq = 0;
        return 1;
    }
    return 0;
}

void rethink_gayle()
{
    int lev2 = 0;
    int lev6 = 0;
    byte mask;

    if (g_curr_conf.cs_ide == IDE_A4000)
    {
        if (isideirq())
            gayle_irq |= GAYLE_IRQ_IDE;
        if ((gayle_irq & GAYLE_IRQ_IDE) && !(intreq & 0x0008))
            INTREQ_0(0x8000 | 0x0008);
    }

    if (g_curr_conf.cs_ide != IDE_A600A1200 && !g_curr_conf.cs_pcmcia)
        return;
    if (isideirq())
        gayle_irq |= GAYLE_IRQ_IDE;
    mask = gayle_int & gayle_irq;
    if (mask & (GAYLE_IRQ_IDE | GAYLE_IRQ_WR))
        lev2 = 1;
    if (mask & GAYLE_IRQ_CCDET)
        lev6 = 1;
    if (mask & (GAYLE_IRQ_BVD1 | GAYLE_IRQ_BVD2))
    {
        if (gayle_int & GAYLE_INT_BVD_LEV)
            lev6 = 1;
        else
            lev2 = 1;
    }
    if (mask & GAYLE_IRQ_BSY)
    {
        if (gayle_int & GAYLE_INT_BSY_LEV)
            lev6 = 1;
        else
            lev2 = 1;
    }
    if (lev2 && !(intreq & 0x0008))
        INTREQ_0(0x8000 | 0x0008);
    if (lev6 && !(intreq & 0x2000))
        INTREQ_0(0x8000 | 0x2000);
}

static void gayle_cs_change(byte mask, int onoff)
{
    int changed = 0;
    if ((gayle_cs & mask) && !onoff)
    {
        gayle_cs &= ~mask;
        changed = 1;
    }
    else if (!(gayle_cs & mask) && onoff)
    {
        gayle_cs |= mask;
        changed = 1;
    }
    if (changed)
    {
        gayle_irq |= mask;
        rethink_gayle();
        if (mask & GAYLE_CS_CCDET)
        {
            if (gayle_irq & GAYLE_IRQ_RESET)
                uae_reset(0);
            if (gayle_irq & GAYLE_IRQ_BERR)
                Exception(2);
        }
    }
}

static void card_trigger(int insert)
{
    if (insert)
    {
        if (pcmcia_card)
        {
            gayle_cs_change(GAYLE_CS_CCDET, 1);
            gayle_cfg = GAYLE_CFG_100NS;
            if (!pcmcia_readonly)
                gayle_cs_change(GAYLE_CS_WR, 1);
        }
    }
    else
    {
        gayle_cfg = 0;
        gayle_cs_change(GAYLE_CS_CCDET, 0);
        gayle_cs_change(GAYLE_CS_BVD2, 0);
        gayle_cs_change(GAYLE_CS_BVD1, 0);
        gayle_cs_change(GAYLE_CS_WR, 0);
        gayle_cs_change(GAYLE_CS_BSY, 0);
    }
    rethink_gayle();
}

static void write_gayle_cfg(byte val)
{
    gayle_cfg = val;
}
static byte read_gayle_cfg()
{
    return gayle_cfg & 0x0f;
}
static void write_gayle_irq(byte val)
{
    gayle_irq = (gayle_irq & val) | (val & 3);
}
static byte read_gayle_irq()
{
    return gayle_irq;
}
static void write_gayle_int(byte val)
{
    gayle_int = val;
}
static byte read_gayle_int()
{
    return gayle_int;
}
static void write_gayle_cs(byte val)
{
    int ov = gayle_cs;

    gayle_cs_mask = val & ~3;
    gayle_cs &= ~3;
    gayle_cs |= val & 3;
    if ((ov & 1) != (gayle_cs & 1))
    {
        gayle_map_pcmcia();
        /* PCMCIA disable -> enable */
        card_trigger(!(gayle_cs & GAYLE_CS_DIS) ? 1 : 0);
        if (Logging::GAYLE_PCMCIA_LOG)
            Logger::Write(L"PCMCIA slot: %s PC=%08X\n", !(gayle_cs & 1) ? L"enabled" : L"disabled", M68K_GETPC);
    }
}
static byte read_gayle_cs()
{
    byte v;

    v = gayle_cs_mask | gayle_cs;
    if (isideirq())
        v |= GAYLE_CS_IDE;
    return v;
}

static void ide_interrupt()
{
    if (ide_devcon & 2)
        return;
    // ide->status |= IDE_STATUS_BSY;
    ide->irq_delay = 2;
}

static void ide_interrupt_do(struct ide_hdf* ide)
{
    ide->status &= ~IDE_STATUS_BSY;
    ide->irq_delay = 0;
    ide->irq = 1;
    rethink_gayle();
}

static void ide_fail_err(byte err)
{
    ide_error |= err;
    if (ide_drv == 1 && idedrive[ide2 + 1]->hdhfd.size == 0)
        idedrive[ide2]->status |= IDE_STATUS_ERR;
    ide->status |= IDE_STATUS_ERR;
    ide_interrupt();
}
static void ide_fail()
{
    ide_fail_err(IDE_ERR_ABRT);
}

static void ide_data_ready()
{
    memset(ide->secbuf, 0, ide->blocksize);
    ide->data_offset = 0;
    ide->status |= IDE_STATUS_DRQ;
    ide->data_size = ide->blocksize;
    ide->data_multi = 1;
    ide_interrupt();
}

static void ide_recalibrate()
{
    Logger::Write(L"IDE%d recalibrate\n", ide->num);
    ide_sector = 0;
    ide_lcyl = ide_hcyl = 0;
    ide_interrupt();
}
static void ide_identify_drive()
{
    ulong64 totalsecs;
    int v;
    byte* buf = ide->secbuf;
    TCHAR tmp[100];

    if (ide->hdhfd.size == 0)
    {
        ide_fail();
        return;
    }
    memset(buf, 0, ide->blocksize);
    if (Logging::GAYLE_IDE_LOG > 0)
        Logger::Write(L"IDE%d identify drive\n", ide->num);
    ide_data_ready();
    ide->data_size *= -1;
    pw(0, 1 << 6);
    pw(1, ide->hdhfd.cyls_def);
    pw(2, 0xc837);
    pw(3, ide->hdhfd.heads_def);
    pw(4, ide->blocksize * ide->hdhfd.secspertrack_def);
    pw(5, ide->blocksize);
    pw(6, ide->hdhfd.secspertrack_def);
    ps(10, L"68000", 20);  /* serial */
    pw(20, 3);
    pw(21, ide->blocksize);
    pw(22, 4);
    ps(23, L"0.3", 8);  /* firmware revision */
    _stprintf(tmp, L"UAE-IDE %s", ide->hdhfd.hfd.product_id);
    ps(27, tmp, 40);  /* model */
    pw(47, MAX_IDE_MULTIPLE_SECTORS >> (ide->blocksize / 512 - 1));  /* max sectors in multiple mode */
    pw(48, 1);
    pw(49, (1 << 9) | (1 << 8));  /* LBA and DMA supported */
    pw(51, 0x200);  /* PIO cycles */
    pw(52, 0x200);  /* DMA cycles */
    pw(53, 1 | 2 | 4);
    pw(54, ide->hdhfd.cyls);
    pw(55, ide->hdhfd.heads);
    pw(56, ide->hdhfd.secspertrack);
    totalsecs = ide->hdhfd.cyls * ide->hdhfd.heads * ide->hdhfd.secspertrack;
    pw(57, (ushort)totalsecs);
    pw(58, (ushort)(totalsecs >> 16));
    v = idedrive[ide_drv]->multiple_mode;
    pw(59, (v > 0 ? 0x100 : 0) | v);
    totalsecs = ide->hdhfd.size / ide->blocksize;
    if (totalsecs > 0x0fffffff)
        totalsecs = 0x0fffffff;
    pw(60, (ushort)totalsecs);
    pw(61, (ushort)(totalsecs >> 16));
    pw(62, 0x0f);
    pw(63, 0x0f);
    pw(64, 0x03);  /* PIO3 and PIO4 */
    pw(65, 120);  /* MDMA2 supported */
    pw(66, 120);
    pw(67, 120);
    pw(68, 120);
    pw(80, (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6));  /* ATA-1 to ATA-6 */
    pw(81, 0x1c);  /* ATA revision */
    pw(82, (1 << 14));  /* NOP command supported */
    pw(83, (1 << 14) | (1 << 13) | (1 << 12) | (ide->lba48 ? (1 << 10) : 0));  /* cache flushes, LBA 48 supported */
    pw(84, 1 << 14);
    pw(85, 1 << 14);
    pw(86, (1 << 14) | (1 << 13) | (1 << 12) | (ide->lba48 ? (1 << 10) : 0));  /* cache flushes, LBA 48 enabled */
    pw(87, 1 << 14);
    pw(88, (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0));  /* UDMA modes */
    pw(93, (1 << 14) | (1 << 13) | (1 << 0));
    if (ide->lba48)
    {
        totalsecs = ide->hdhfd.size / ide->blocksize;
        pw(100, (ushort)(totalsecs >> 0));
        pw(101, (ushort)(totalsecs >> 16));
        pw(102, (ushort)(totalsecs >> 32));
        pw(103, (ushort)(totalsecs >> 48));
    }
}

static void ide_execute_drive_diagnostics()
{
    ide_error = 1;
    ide_sector = ide_nsector = 1;
    ide_select = 0;
    ide_lcyl = ide_hcyl = 0;
    ide->status &= ~IDE_STATUS_BSY;
}

static void ide_initialize_drive_parameters()
{
    if (ide->hdhfd.size)
    {
        ide->hdhfd.secspertrack = ide_nsector == 0 ? 256 : ide_nsector;
        ide->hdhfd.heads = (ide_select & 15) + 1;
        ide->hdhfd.cyls = (ide->hdhfd.size / ide->blocksize) / (ide->hdhfd.secspertrack * ide->hdhfd.heads);
        if (ide->hdhfd.heads * ide->hdhfd.cyls * ide->hdhfd.secspertrack > 16515072 || ide->lba48)
        {
            ide->hdhfd.cyls = ide->hdhfd.cyls_def;
            ide->hdhfd.heads = ide->hdhfd.heads_def;
            ide->hdhfd.secspertrack = ide->hdhfd.secspertrack_def;
        }
    }
    else
    {
        ide_error |= IDE_ERR_ABRT;
        ide->status |= IDE_STATUS_ERR;
    }
    Logger::Write(L"IDE%d initialize drive parameters, CYL=%d,SPT=%d,HEAD=%d\n",
        ide->num, ide->hdhfd.cyls, ide->hdhfd.secspertrack, ide->hdhfd.heads);
    ide_interrupt();
}
static void ide_set_multiple_mode()
{
    Logger::Write(L"IDE%d drive multiple mode = %d\n", ide->num, ide_nsector);
    ide->multiple_mode = ide_nsector;
    ide_interrupt();
}
static void ide_set_features()
{
    // int type = ide_nsector >> 3;
    // int mode = ide_nsector & 7;

    Logger::Write(L"IDE%d set features %02X (%02X)\n", ide->num, ide_feat, ide_nsector);
    ide_fail();
}

static void get_lbachs(struct ide_hdf* ide, ulong64* lbap, uint* cyl, uint* head, uint* sec, int lba48)
{
    if (lba48 && (ide_select & 0x40))
    {
        ulong64 lba;
        lba = (ide_hcyl << 16) | (ide_lcyl << 8) | ide_sector;
        lba |= ((ide_hcyl2 << 16) | (ide_lcyl2 << 8) | ide_sector2) << 24;
        *lbap = lba;
    }
    else
    {
        if (ide_select & 0x40)
        {
            *lbap = ((ide_select & 15) << 24) | (ide_hcyl << 16) | (ide_lcyl << 8) | ide_sector;
        }
        else
        {
            *cyl = (ide_hcyl << 8) | ide_lcyl;
            *head = ide_select & 15;
            *sec = ide_sector;
            *lbap = (((*cyl) * ide->hdhfd.heads + (*head)) * ide->hdhfd.secspertrack) + (*sec) - 1;
        }
    }
}

static int get_nsec(int lba48)
{
    if (lba48)
        return (ide_nsector == 0 && ide_nsector2 == 0) ? 65536 : (ide_nsector2 * 256 + ide_nsector);
    else
        return ide_nsector == 0 ? 256 : ide_nsector;
}
static int dec_nsec(int lba48, int v)
{
    if (lba48)
    {
        ushort nsec;
        nsec = ide_nsector2 * 256 + ide_nsector;
        ide_nsector -= v;
        ide_nsector2 = nsec >> 8;
        ide_nsector = nsec & 0xff;
        return (ide_nsector2 << 8) | ide_nsector;
    }
    else
    {
        ide_nsector -= v;
        return ide_nsector;
    }
}

static void put_lbachs(struct ide_hdf* ide, ulong64 lba, uint cyl, uint head, uint sec, uint inc, int lba48)
{
    if (lba48)
    {
        lba += inc;
        ide_hcyl = (lba >> 16) & 0xff;
        ide_lcyl = (lba >> 8) & 0xff;
        ide_sector = lba & 0xff;
        lba >>= 24;
        ide_hcyl2 = (lba >> 16) & 0xff;
        ide_lcyl2 = (lba >> 8) & 0xff;
        ide_sector2 = lba & 0xff;
    }
    else
    {
        if (ide_select & 0x40)
        {
            lba += inc;
            ide_select &= ~15;
            ide_select |= (lba >> 24) & 15;
            ide_hcyl = (lba >> 16) & 0xff;
            ide_lcyl = (lba >> 8) & 0xff;
            ide_sector = lba & 0xff;
        }
        else
        {
            sec += inc;
            while (sec >= ide->hdhfd.secspertrack)
            {
                sec -= ide->hdhfd.secspertrack;
                head++;
                if (head >= ide->hdhfd.heads)
                {
                    head -= ide->hdhfd.heads;
                    cyl++;
                }
            }
            ide_select &= ~15;
            ide_select |= head;
            ide_sector = sec;
            ide_hcyl = cyl >> 8;
            ide_lcyl = (byte)cyl;
        }
    }
}

static void check_maxtransfer(int state)
{
    if (state == 1)
    {
        // transfer was started
        if (ide->maxtransferstate < 2 && ide_nsector == 0)
        {
            ide->maxtransferstate = 1;
        }
        else if (ide->maxtransferstate == 2)
        {
            // second transfer was started (part of split)
            Logger::Write(L"IDE maxtransfer check detected split >256 block transfer\n");
            ide->maxtransferstate = 0;
        }
        else
        {
            ide->maxtransferstate = 0;
        }
    }
    else if (state == 2)
    {
        // address was read
        if (ide->maxtransferstate == 1)
            ide->maxtransferstate++;
        else
            ide->maxtransferstate = 0;
    }
}

static void ide_read_sectors(int flags)
{
    uint cyl, head, sec, nsec;
    ulong64 lba;
    int multi = flags & 1;
    int lba48 = flags & 2;

    if (multi && ide->multiple_mode == 0)
    {
        ide_fail();
        return;
    }
    check_maxtransfer(1);
    gui_flicker_led(LED_HD, ide->num, 1);
    nsec = get_nsec(lba48);
    get_lbachs(ide, &lba, &cyl, &head, &sec, lba48);
    if (Logging::GAYLE_IDE_LOG > 0)
        Logger::Write(L"IDE%d read off=%d, sec=%d (%d) lba%d\n", ide->num, (uint)lba, nsec, ide->multiple_mode, lba48 ? 48 : 28);
    if (lba * ide->blocksize >= ide->hdhfd.size)
    {
        ide_data_ready();
        ide_fail_err(IDE_ERR_IDNF);
        return;
    }
    ide->data_multi = multi ? ide->multiple_mode : 1;
    ide->data_offset = 0;
    ide->status |= IDE_STATUS_DRQ;
    ide->data_size = nsec * ide->blocksize;
    ide_interrupt();
}

static void ide_write_sectors(int flags)
{
    uint cyl, head, sec, nsec;
    ulong64 lba;
    int multi = flags & 1;
    int lba48 = flags & 2;

    if (multi && ide->multiple_mode == 0)
    {
        ide_fail();
        return;
    }
    check_maxtransfer(1);
    gui_flicker_led(LED_HD, ide->num, 2);
    nsec = get_nsec(lba48);
    get_lbachs(ide, &lba, &cyl, &head, &sec, lba48);
    if (lba * ide->blocksize >= ide->hdhfd.size)
    {
        ide_data_ready();
        ide_fail_err(IDE_ERR_IDNF);
        return;
    }
    if (Logging::GAYLE_IDE_LOG > 0)
        Logger::Write(L"IDE%d write off=%d, sec=%d (%d) lba%d\n", ide->num, (uint)lba, nsec, ide->multiple_mode, lba48 ? 48 : 28);
    if (nsec * ide->blocksize > ide->hdhfd.size - lba * ide->blocksize)
        nsec = (ide->hdhfd.size - lba * ide->blocksize) / ide->blocksize;
    if (nsec <= 0)
    {
        ide_data_ready();
        ide_fail_err(IDE_ERR_IDNF);
        return;
    }
    ide->data_multi = multi ? ide->multiple_mode : 1;
    ide->data_offset = 0;
    ide->status |= IDE_STATUS_DRQ;
    ide->data_size = nsec * ide->blocksize;
}

static void ide_do_command(byte cmd)
{
    int lba48 = ide->lba48;

    if (Logging::GAYLE_IDE_LOG > 1)
        Logger::Write(L"**** IDE%d command %02X\n", ide->num, cmd);
    ide->status &= ~(IDE_STATUS_DRDY | IDE_STATUS_DRQ | IDE_STATUS_ERR);
    ide_error = 0;

    if (cmd == 0x10)   /* recalibrate */
    {
        ide_recalibrate();
    }
    else if (cmd == 0xec)     /* identify drive */
    {
        ide_identify_drive();
    }
    else if (cmd == 0x90)     /* execute drive diagnostics */
    {
        ide_execute_drive_diagnostics();
    }
    else if (cmd == 0x91)     /* initialize drive parameters */
    {
        ide_initialize_drive_parameters();
    }
    else if (cmd == 0xc6)     /* set multiple mode */
    {
        ide_set_multiple_mode();
    }
    else if (cmd == 0x20 || cmd == 0x21)     /* read sectors */
    {
        ide_read_sectors(0);
    }
    else if (cmd == 0x24 && lba48)     /* read sectors ext */
    {
        ide_read_sectors(2);
    }
    else if (cmd == 0xc4)     /* read multiple */
    {
        ide_read_sectors(1);
    }
    else if (cmd == 0x29 && lba48)     /* read multiple ext */
    {
        ide_read_sectors(1 | 2);
    }
    else if (cmd == 0x30 || cmd == 0x31)     /* write sectors */
    {
        ide_write_sectors(0);
    }
    else if (cmd == 0x34 && lba48)     /* write sectors ext */
    {
        ide_write_sectors(2);
    }
    else if (cmd == 0xc5)     /* write multiple */
    {
        ide_write_sectors(1);
    }
    else if (cmd == 0x39 && lba48)     /* write multiple ext */
    {
        ide_write_sectors(1 | 2);
    }
    else if (cmd == 0x50)     /* format track (nop) */
    {
        ide_interrupt();
    }
    else if (cmd == 0xa1)     /* ATAPI identify (IDE HD is not ATAPI) */
    {
        ide_fail();
    }
    else if (cmd == 0xef)     /* set features  */
    {
        ide_set_features();
    }
    else if (cmd == 0x00)     /* nop */
    {
        ide_fail();
    }
    else if (cmd == 0xe0 || cmd == 0xe1 || cmd == 0xe7 || cmd == 0xea)     /* standby now/idle/flush cache/flush cache ext */
    {
        ide_interrupt();
    }
    else if (cmd == 0xe5)     /* check power mode */
    {
        ide_nsector = 0xff;
        ide_interrupt();
    }
    else
    {
        ide_fail();
        Logger::Write(L"IDE%d: unknown command %x\n", ide->num, cmd);
    }
}

static ushort ide_get_data()
{
    uint cyl = 0, head = 0, sec = 0, nsec;
    ulong64 lba = 0;
    bool irq = false;
    bool last = false;
    ushort v;

    if (Logging::GAYLE_IDE_LOG > 4)
        Logger::Write(L"IDE%d DATA read\n", ide->num);
    if (ide->data_size == 0)
    {
        if (Logging::GAYLE_IDE_LOG > 0)
            Logger::Write(L"IDE%d DATA read without DRQ!?\n", ide->num);
        if (ide->hdhfd.size == 0)
            return 0xffff;
        return 0;
    }
    nsec = 0;
    if (ide->data_offset == 0 && ide->data_size >= 0)
    {
        get_lbachs(ide, &lba, &cyl, &head, &sec, ide->lba48);
        nsec = get_nsec(ide->lba48);
        if (nsec * ide->blocksize > ide->hdhfd.size - lba * ide->blocksize)
            nsec = (ide->hdhfd.size - lba * ide->blocksize) / ide->blocksize;
        if (nsec <= 0)
        {
            ide_data_ready();
            ide_fail_err(IDE_ERR_IDNF);
            return 0;
        }
        if (nsec > ide->data_multi)
            nsec = ide->data_multi;
        hdf_read(&ide->hdhfd.hfd, ide->secbuf, lba * ide->blocksize, nsec * ide->blocksize);
        if (!dec_nsec(ide->lba48, nsec))
            last = true;
        if (Logging::GAYLE_IDE_LOG > 1)
            Logger::Write(L"IDE%d read, read %d bytes to buffer\n", ide->num, nsec * ide->blocksize);
    }

    v = ide->secbuf[ide->data_offset + 1] | (ide->secbuf[ide->data_offset + 0] << 8);
    ide->data_offset += 2;
    if (ide->data_size < 0)
    {
        ide->data_size += 2;
    }
    else
    {
        ide->data_size -= 2;
        if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0)
        {
            irq = true;
            ide->data_offset = 0;
        }
    }
    if (ide->data_size == 0)
    {
        ide->status &= ~IDE_STATUS_DRQ;
        if (Logging::GAYLE_IDE_LOG > 1)
            Logger::Write(L"IDE%d read finished\n", ide->num);
    }
    if (nsec)
    {
        put_lbachs(ide, lba, cyl, head, sec, last ? nsec - 1 : nsec, ide->lba48);
    }
    if (irq)
    {
        ide_interrupt();
    }
    return v;
}

static void ide_write_drive(bool last)
{
    uint cyl, head, sec, nsec;
    ulong64 lba;

    nsec = ide->data_offset / ide->blocksize;
    if (!nsec)
        return;
    get_lbachs(ide, &lba, &cyl, &head, &sec, ide->lba48);
    hdf_write(&ide->hdhfd.hfd, ide->secbuf, lba * ide->blocksize, ide->data_offset);
    put_lbachs(ide, lba, cyl, head, sec, last ? nsec - 1 : nsec, ide->lba48);
    dec_nsec(ide->lba48, nsec);
    if (Logging::GAYLE_IDE_LOG > 1)
        Logger::Write(L"IDE%d write interrupt, %d bytes written\n", ide->num, ide->data_offset);
    ide->data_offset = 0;
}

static void ide_put_data(ushort v)
{
    int irq = 0;

    if (Logging::GAYLE_IDE_LOG > 4)
        Logger::Write(L"IDE%d DATA write %04x %d/%d\n", ide->num, v, ide->data_offset, ide->data_size);
    if (ide->data_size == 0)
    {
        if (Logging::GAYLE_IDE_LOG > 0)
            Logger::Write(L"IDE%d DATA write without DRQ!?\n", ide->num);
        return;
    }
    ide->secbuf[ide->data_offset + 1] = v & 0xff;
    ide->secbuf[ide->data_offset + 0] = v >> 8;
    ide->data_offset += 2;
    ide->data_size -= 2;
    if (ide->data_size == 0)
    {
        irq = 1;
        ide_write_drive(true);
        ide->status &= ~IDE_STATUS_DRQ;
        if (Logging::GAYLE_IDE_LOG > 1)
            Logger::Write(L"IDE%d write finished\n", ide->num);
    }
    else if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0)
    {
        irq = 1;
        ide_write_drive(false);
    }
    if (irq)
        ide_interrupt();
}

static int get_ide_reg(uae_ptr addr)
{
    // uae_ptr a = addr;
    addr &= 0xffff;
    if (addr >= 0x3020 && addr <= 0x3021 && g_curr_conf.cs_ide == IDE_A4000)
        return -1;
    addr &= ~0x2020;
    addr >>= 2;
    ide2 = 0;
    if (addr & 0x400)
    {
        if (ide_splitter)
        {
            ide2 = 2;
            addr &= ~0x400;
        }
    }
    ide = idedrive[ide_drv + ide2];
    return addr;
}

static uint ide_read(uae_ptr addr)
{
    int ide_reg;
    byte v = 0;

    addr &= 0xffff;
    if ((Logging::GAYLE_IDE_LOG > 2 && (addr != 0x2000 && addr != 0x2001 && addr != 0x2020 && addr != 0x2021 && addr != GAYLE_IRQ_1200)) || Logging::GAYLE_IDE_LOG > 4)
        Logger::Write(L"IDE_READ %08X PC=%X\n", addr, M68K_GETPC);
    if (g_curr_conf.cs_ide <= 0)
    {
        if (addr == 0x201c) // AR1200 IDE detection hack
            return 0x7f;
        return 0xff;
    }
    if (addr >= GAYLE_IRQ_4000 && addr <= GAYLE_IRQ_4000 + 1 && g_curr_conf.cs_ide == IDE_A4000)
    {
        byte v = gayle_irq;
        gayle_irq = 0;
        return v;
    }
    if (addr >= 0x4000)
    {
        if (addr == GAYLE_IRQ_1200)
        {
            if (g_curr_conf.cs_ide == IDE_A600A1200)
                return read_gayle_irq();
            return 0;
        }
        else if (addr == GAYLE_INT_1200)
        {
            if (g_curr_conf.cs_ide == IDE_A600A1200)
                return read_gayle_int();
            return 0;
        }
        return 0;
    }
    ide_reg = get_ide_reg(addr);
    /* Emulated "ide killer". Prevents long KS boot delay if no drives installed */
    if (idedrive[0]->hdhfd.size == 0 && idedrive[2]->hdhfd.size == 0)
    {
        if (ide_reg == IDE_STATUS)
            return 0x7f;
        return 0xff;
    }
    switch (ide_reg)
    {
        case IDE_DRVADDR:
            v = ((ide_drv ? 2 : 1) | ((ide_select & 15) << 2)) ^ 0xff;
            break;
        case IDE_DATA:
            break;
        case IDE_ERROR:
            v = ide_error;
            break;
        case IDE_NSECTOR:
            if (ide_devcon & 0x80)
                v = ide_nsector2;
            else
                v = ide_nsector;
            break;
        case IDE_SECTOR:
            if (ide_devcon & 0x80)
                v = ide_sector2;
            else
                v = ide_sector;
            check_maxtransfer(2);
            break;
        case IDE_LCYL:
            if (ide_devcon & 0x80)
                v = ide_lcyl2;
            else
                v = ide_lcyl;
            break;
        case IDE_HCYL:
            if (ide_devcon & 0x80)
                v = ide_hcyl2;
            else
                v = ide_hcyl;
            break;
        case IDE_SELECT:
            v = ide_select;
            break;
        case IDE_STATUS:
            ide->irq = 0; /* fall through */
        case IDE_DEVCON: /* ALTSTATUS when reading */
            if (ide->hdhfd.size == 0)
            {
                v = 0;
                if (ide_error)
                    v |= IDE_STATUS_ERR;
            }
            else
            {
                v = ide->status;
                v |= IDE_STATUS_DRDY | IDE_STATUS_DSC;
            }
            break;
    }
    if (Logging::GAYLE_IDE_LOG > 2 && ide_reg > 0)
        Logger::Write(L"IDE%d register %d->%02X\n", ide->num, ide_reg, (uint)v & 0xff);
    return v;
}

static void ide_write(uae_ptr addr, uint val)
{
    int ide_reg;

    if ((Logging::GAYLE_IDE_LOG > 2 && (addr != 0x2000 && addr != 0x2001 && addr != 0x2020 && addr != 0x2021 && addr != GAYLE_IRQ_1200)) || Logging::GAYLE_IDE_LOG > 4)
        Logger::Write(L"IDE_WRITE %08X=%02X PC=%X\n", addr, (uint)val & 0xff, M68K_GETPC);
    if (g_curr_conf.cs_ide <= 0)
        return;
    if (g_curr_conf.cs_ide == IDE_A600A1200)
    {
        if (addr == GAYLE_IRQ_1200)
        {
            write_gayle_irq(val);
            return;
        }
        if (addr == GAYLE_INT_1200)
        {
            write_gayle_int(val);
            return;
        }
    }
    if (addr >= 0x4000)
        return;
    ide_devcon &= ~0x80; /* clear HOB */
    ide_reg = get_ide_reg(addr);
    if (Logging::GAYLE_IDE_LOG > 2 && ide_reg > 0)
        Logger::Write(L"IDE%d register %d=%02X\n", ide->num, ide_reg, (uint)val & 0xff);
    switch (ide_reg)
    {
        case IDE_DRVADDR:
            break;
        case IDE_DEVCON:
            if ((ide_devcon & 4) == 0 && (val & 4) != 0)
                ide_execute_drive_diagnostics();
            ide_devcon = val;
            break;
        case IDE_DATA:
            break;
        case IDE_ERROR:
            ide_feat2 = ide_feat;
            ide_feat = val;
            break;
        case IDE_NSECTOR:
            ide_nsector2 = ide_nsector;
            ide_nsector = val;
            break;
        case IDE_SECTOR:
            ide_sector2 = ide_sector;
            ide_sector = val;
            break;
        case IDE_LCYL:
            ide_lcyl2 = ide_lcyl;
            ide_lcyl = val;
            break;
        case IDE_HCYL:
            ide_hcyl2 = ide_hcyl;
            ide_hcyl = val;
            break;
        case IDE_SELECT:
            ide_select = val;
            ide_drv = (val & 0x10) ? 1 : 0;
            break;
        case IDE_STATUS:
            ide->irq = 0;
            ide_do_command(val);
            break;
    }
}

static int gayle_read(uae_ptr addr)
{
    uae_ptr oaddr = addr;
    uint v = 0;
    int got = 0;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (g_curr_conf.cs_ide == IDE_A600A1200)
    {
        if ((addr & 0xA0000) != 0xA0000)
            return 0;
    }
    addr &= 0xffff;
    if (g_curr_conf.cs_pcmcia)
    {
        if (g_curr_conf.cs_ide != IDE_A600A1200)
        {
            if (addr == GAYLE_IRQ_1200)
            {
                v = read_gayle_irq();
                got = 1;
            }
            else if (addr == GAYLE_INT_1200)
            {
                v = read_gayle_int();
                got = 1;
            }
        }
        if (addr == GAYLE_CS_1200)
        {
            v = read_gayle_cs();
            got = 1;
            if (Logging::GAYLE_PCMCIA_LOG)
                Logger::Write(L"PCMCIA STATUS READ %08X=%02X PC=%08X\n", oaddr, (uint)v & 0xff, M68K_GETPC);
        }
        else if (addr == GAYLE_CFG_1200)
        {
            v = read_gayle_cfg();
            got = 1;
            if (Logging::GAYLE_PCMCIA_LOG)
                Logger::Write(L"PCMCIA CONFIG READ %08X=%02X PC=%08X\n", oaddr, (uint)v & 0xff, M68K_GETPC);
        }
    }
    if (!got)
        v = ide_read(addr);
    if (Logging::GAYLE_LOG)
        Logger::Write(L"GAYLE_READ %08X=%02X PC=%08X\n", oaddr, (uint)v & 0xff, M68K_GETPC);
    return v;
}

static void gayle_write(uae_ptr addr, int val)
{
    uae_ptr oaddr = addr;
    int got = 0;
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (g_curr_conf.cs_ide == IDE_A600A1200)
    {
        if ((addr & 0xA0000) != 0xA0000)
            return;
    }
    addr &= 0xffff;
    if (g_curr_conf.cs_pcmcia)
    {
        if (g_curr_conf.cs_ide != IDE_A600A1200)
        {
            if (addr == GAYLE_IRQ_1200)
            {
                write_gayle_irq(val);
                got = 1;
            }
            else if (addr == GAYLE_INT_1200)
            {
                write_gayle_int(val);
                got = 1;
            }
        }
        if (addr == GAYLE_CS_1200)
        {
            write_gayle_cs(val);
            got = 1;
            if (Logging::GAYLE_PCMCIA_LOG > 1)
                Logger::Write(L"PCMCIA STATUS WRITE %08X=%02X PC=%08X\n", oaddr, (uint)val & 0xff, M68K_GETPC);
        }
        else if (addr == GAYLE_CFG_1200)
        {
            write_gayle_cfg(val);
            got = 1;
            if (Logging::GAYLE_PCMCIA_LOG > 1)
                Logger::Write(L"PCMCIA CONFIG WRITE %08X=%02X PC=%08X\n", oaddr, (uint)val & 0xff, M68K_GETPC);
        }
    }

    if (Logging::GAYLE_LOG)
        Logger::Write(L"GAYLE_WRITE %08X=%02X PC=%08X\n", addr, (uint)val & 0xff, M68K_GETPC);
    if (!got)
        ide_write(addr, val);
}

static uint REGPARAM3 gayle_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle_bget(uae_ptr) REGPARAM;
static void REGPARAM3 gayle_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle_bput(uae_ptr, uint) REGPARAM;

addrbank gayle_bank = {
    gayle_lget, gayle_wget, gayle_bget,
    gayle_lput, gayle_wput, gayle_bput,
    default_xlate, default_check, nullptr, L"Gayle (low)",
    dummy_lgeti, dummy_wgeti, ABFLAG_IO
};

static int isa4000t(uae_ptr addr)
{
    if (g_curr_conf.cs_mbdmac != 2)
        return 0;
    if ((addr & 0xffff) >= (GAYLE_BASE_4000 & 0xffff))
        return 0;
    return 1;
}

static uint REGPARAM2 gayle_lget(uae_ptr addr)
{
    uint v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = gayle_wget(addr) << 16;
    v |= gayle_wget(addr + 2);
    return v;
}
static uint REGPARAM2 gayle_wget(uae_ptr addr)
{
    int ide_reg;
    ushort v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (isa4000t(addr))
    {
        addr -= NCR_OFFSET;
        return (ncr_bget2(addr) << 8) | ncr_bget2(addr + 1);
    }
    ide_reg = get_ide_reg(addr);
    if (ide_reg == IDE_DATA)
        return ide_get_data();
    v = gayle_bget(addr) << 8;
    v |= gayle_bget(addr + 1);
    return v;
}
static uint REGPARAM2 gayle_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (isa4000t(addr))
    {
        addr -= NCR_OFFSET;
        return ncr_bget2(addr);
    }
    return gayle_read(addr);
}

static void REGPARAM2 gayle_lput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle_wput(addr, value >> 16);
    gayle_wput(addr + 2, value & 0xffff);
}
static void REGPARAM2 gayle_wput(uae_ptr addr, uint value)
{
    int ide_reg;
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (isa4000t(addr))
    {
        addr -= NCR_OFFSET;
        ncr_bput2(addr, value >> 8);
        ncr_bput2(addr + 1, value);
        return;
    }
    ide_reg = get_ide_reg(addr);
    if (ide_reg == IDE_DATA)
    {
        ide_put_data(value);
        return;
    }
    gayle_bput(addr, value >> 8);
    gayle_bput(addr + 1, value & 0xff);
}

static void REGPARAM2 gayle_bput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (isa4000t(addr))
    {
        addr -= NCR_OFFSET;
        ncr_bput2(addr, value);
        return;
    }
    gayle_write(addr, value);
}

static void gayle2_write(uae_ptr addr, uint v)
{
    gayle_id_cnt = 0;
}

static uint gayle2_read(uae_ptr addr)
{
    byte v = 0;
    addr &= 0xffff;
    if (addr == 0x1000)
    {
        /* Gayle ID */
        if (gayle_id_cnt == 0 || gayle_id_cnt == 1 || gayle_id_cnt == 3 || ((g_curr_conf.chipset_mask & CSMASK_AGA) && gayle_id_cnt == 7) ||
            (g_curr_conf.cs_cd32cd && !g_curr_conf.cs_ide && !g_curr_conf.cs_pcmcia && gayle_id_cnt == 2))
        {
            v = 0x80;
        }
        else
            v = 0x00;
        gayle_id_cnt++;
    }
    return v;
}

static uint REGPARAM3 gayle2_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle2_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle2_bget(uae_ptr) REGPARAM;
static void REGPARAM3 gayle2_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle2_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle2_bput(uae_ptr, uint) REGPARAM;

addrbank gayle2_bank = {
    gayle2_lget, gayle2_wget, gayle2_bget,
    gayle2_lput, gayle2_wput, gayle2_bput,
    default_xlate, default_check, nullptr, L"Gayle (high)",
    dummy_lgeti, dummy_wgeti, ABFLAG_IO
};

static uint REGPARAM2 gayle2_lget(uae_ptr addr)
{
    uint v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = gayle2_wget(addr) << 16;
    v |= gayle2_wget(addr + 2);
    return v;
}
static uint REGPARAM2 gayle2_wget(uae_ptr addr)
{
    ushort v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = gayle2_bget(addr) << 8;
    v |= gayle2_bget(addr + 1);
    return v;
}
static uint REGPARAM2 gayle2_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return gayle2_read(addr);
}

static void REGPARAM2 gayle2_lput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle2_wput(addr, value >> 16);
    gayle2_wput(addr + 2, value & 0xffff);
}

static void REGPARAM2 gayle2_wput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle2_bput(addr, value >> 8);
    gayle2_bput(addr + 1, value & 0xff);
}

static void REGPARAM2 gayle2_bput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle2_write(addr, value);
}

static byte ramsey_config;
static int gary_coldboot, gary_toenb, gary_timeout;
static int garyidoffset;

static void mbres_write(uae_ptr addr, uint val, int size)
{
    addr &= 0xffff;

    if (Logging::GAYLE_MBRES_LOG > 0)
        Logger::Write(L"MBRES_WRITE %08X=%08X (%d) PC=%08X S=%d\n", addr, val, size, M68K_GETPC, regs.s);
    if (1 || regs.s)   /* CPU FC = supervisor only */
    {
        uint addr2 = addr & 3;
        uint addr64 = (addr >> 6) & 3;
        if (addr == 0x1002)
            garyidoffset = -1;
        if (addr64 == 0 && addr2 == 0x03)
            ramsey_config = val;
        if (addr2 == 0x02)
            gary_coldboot = (val & 0x80) ? 1 : 0;
        if (addr2 == 0x01)
            gary_toenb = (val & 0x80) ? 1 : 0;
        if (addr2 == 0x00)
            gary_timeout = (val & 0x80) ? 1 : 0;
    }
}

static uint mbres_read(uae_ptr addr, int size)
{
    uint v = 0;
    addr &= 0xffff;

    if (1 || regs.s)   /* CPU FC = supervisor only (only newest ramsey/gary? never implemented?) */
    {
        uint addr2 = addr & 3;
        uint addr64 = (addr >> 6) & 3;
        /* Gary ID (I don't think this exists in real chips..) */
        if (addr == 0x1002 && g_curr_conf.cs_fatgaryrev >= 0)
        {
            garyidoffset++;
            garyidoffset &= 7;
            v = (g_curr_conf.cs_fatgaryrev << garyidoffset) & 0x80;
        }
        for (;;)
        {
            if (addr64 == 1 && addr2 == 0x03)   /* RAMSEY revision */
            {
                if (g_curr_conf.cs_ramseyrev >= 0)
                    v = g_curr_conf.cs_ramseyrev;
                break;
            }
            if (addr64 == 0 && addr2 == 0x03)   /* RAMSEY config */
            {
                if (g_curr_conf.cs_ramseyrev >= 0)
                    v = ramsey_config;
                break;
            }
            if (addr2 == 0x03)
            {
                v = 0xff;
                break;
            }
            if (addr2 == 0x02)   /* coldreboot flag */
            {
                if (g_curr_conf.cs_fatgaryrev >= 0)
                    v = gary_coldboot ? 0x80 : 0x00;
            }
            if (addr2 == 0x01)   /* toenb flag */
            {
                if (g_curr_conf.cs_fatgaryrev >= 0)
                    v = gary_toenb ? 0x80 : 0x00;
            }
            if (addr2 == 0x00)   /* timeout flag */
            {
                if (g_curr_conf.cs_fatgaryrev >= 0)
                    v = gary_timeout ? 0x80 : 0x00;
            }
            v |= 0x7f;
            break;
        }
    }
    else
    {
        v = 0xff;
    }
    if (Logging::GAYLE_MBRES_LOG > 0)
        Logger::Write(L"MBRES_READ %08X=%08X (%d) PC=%08X S=%d\n", addr, v, size, M68K_GETPC, regs.s);
    return v;
}

static uint REGPARAM3 mbres_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 mbres_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 mbres_bget(uae_ptr) REGPARAM;
static void REGPARAM3 mbres_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 mbres_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 mbres_bput(uae_ptr, uint) REGPARAM;

static uint REGPARAM2 mbres_lget(uae_ptr addr)
{
    uint v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = mbres_wget(addr) << 16;
    v |= mbres_wget(addr + 2);
    return v;
}
static uint REGPARAM2 mbres_wget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return mbres_read(addr, 2);
}
static uint REGPARAM2 mbres_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return mbres_read(addr, 1);
}

static void REGPARAM2 mbres_lput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    mbres_wput(addr, value >> 16);
    mbres_wput(addr + 2, value & 0xffff);
}

static void REGPARAM2 mbres_wput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    mbres_write(addr, value, 2);
}

static void REGPARAM2 mbres_bput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    mbres_write(addr, value, 1);
}

addrbank mbres_bank = {
    mbres_lget, mbres_wget, mbres_bget,
    mbres_lput, mbres_wput, mbres_bput,
    default_xlate, default_check, nullptr, L"Motherboard Resources",
    dummy_lgeti, dummy_wgeti, ABFLAG_IO
};

void gayle_hsync()
{
    int i;

    for (i = 0; i < 4; i++)
    {
        struct ide_hdf* ide = idedrive[i];
        if (ide->irq_delay > 0)
        {
            ide->irq_delay--;
            if (ide->irq_delay == 0)
                ide_interrupt_do(ide);
        }
    }
}

static int pcmcia_common_size, pcmcia_attrs_size;
static int pcmcia_common_mask;
static byte* pcmcia_common;
static byte* pcmcia_attrs;
static int pcmcia_write_min, pcmcia_write_max;

static uint gayle_attr_read(uae_ptr addr)
{
    byte v = 0;

    if (Logging::GAYLE_PCMCIA_LOG > 1)
        Logger::Write(L"PCMCIA ATTR R: %x %x\n", addr, M68K_GETPC);
    addr &= 0x80000 - 1;
    if (addr >= 0x40000)
    {
        if (Logging::GAYLE_PCMCIA_LOG > 0)
            Logger::Write(L"GAYLE: Reset disabled\n");
        return v;
    }
    if (addr >= pcmcia_attrs_size)
        return v;
    v = pcmcia_attrs[addr / 2];
    return v;
}
static void gayle_attr_write(uae_ptr addr, uint v)
{
    if (Logging::GAYLE_PCMCIA_LOG > 1)
        Logger::Write(L"PCMCIA ATTR W: %x=%x %x\n", addr, v, M68K_GETPC);
    addr &= 0x80000 - 1;
    if (addr >= 0x40000)
    {
        if (Logging::GAYLE_PCMCIA_LOG > 0)
            Logger::Write(L"GAYLE: Reset active\n");
    }
    else if (addr < pcmcia_attrs_size)
    {
    }
}

static void initsramattr(int size, int readonly)
{
    byte* rp;
    byte* p = pcmcia_attrs;
    int sm, su, code, units;
    struct hardfiledata* hfd = &pcmcia_sram->hfd;
    int real = hfd->flags & HFD_FLAGS_REALDRIVE;

    code = 0;
    su = 512;
    sm = 16384;
    while (size > sm)
    {
        sm *= 4;
        su *= 4;
        code++;
    }
    units = 31 - ((sm - size) / su);

    /* CISTPL_DEVICE */
    *p++ = 0x01;
    *p++ = 3;
    *p++ = (6 /* DTYPE_SRAM */ << 4) | (readonly ? 8 : 0) | (4 /* SPEED_100NS */);
    *p++ = (units << 3) | code; /* memory card size in weird units */
    *p++ = 0xff;

    /* CISTPL_DEVICEGEO */
    *p++ = 0x1e;
    *p++ = 7;
    *p++ = 2; /* 16-bit PCMCIA */
    *p++ = 0;
    *p++ = 1;
    *p++ = 1;
    *p++ = 1;
    *p++ = 1;
    *p++ = 0xff;

    /* CISTPL_VERS_1 */
    *p++ = 0x15;
    rp = p++;
    *p++ = 4; /* PCMCIA 2.1 */
    *p++ = 1;
    if (real)
    {
        Unicode::ua_copy((char*)p, -1, hfd->product_id);
        p += strlen((char*)p) + 1;
        Unicode::ua_copy((char*)p, -1, hfd->product_rev);
    }
    else
    {
        strcpy((char*)p, "UAE");
        p += strlen((char*)p) + 1;
        strcpy((char*)p, "68000");
    }
    p += strlen((char*)p) + 1;
    sprintf((char*)p, "Generic Emulated %dKB PCMCIA SRAM Card", size >> 10);
    p += strlen((char*)p) + 1;
    *p++ = 0;
    *p++ = 0xff;
    *rp = p - rp;

    /* CISTPL_FUNCID */
    *p++ = 0x21;
    *p++ = 2;
    *p++ = 1; /* Memory Card */
    *p++ = 0;

    /* CISTPL_MANFID */
    *p++ = 0x20;
    *p++ = 4;
    *p++ = 0xff;
    *p++ = 0xff;
    *p++ = 1;
    *p++ = 1;

    /* CISTPL_END */
    *p++ = 0xff;
}

static void checkflush(int addr)
{
    if (pcmcia_card == 0 || pcmcia_sram == 0)
        return;
    if (addr >= 0 && pcmcia_common[0] == 0 && pcmcia_common[1] == 0 && pcmcia_common[2] == 0)
        return;  // do not flush periodically if used as a ram expension
    if (addr < 0)
    {
        pcmcia_write_min = 0;
        pcmcia_write_max = pcmcia_common_size;
    }
    if (pcmcia_write_min >= 0)
    {
        if (abs(pcmcia_write_min - addr) >= 512 || abs(pcmcia_write_max - addr) >= 512)
        {
            int blocksize = pcmcia_sram->hfd.blocksize;
            int mask = ~(blocksize - 1);
            int start = pcmcia_write_min & mask;
            int end = (pcmcia_write_max + blocksize - 1) & mask;
            int len = end - start;
            if (len > 0)
            {
                hdf_write(&pcmcia_sram->hfd, pcmcia_common + start, start, len);
                pcmcia_write_min = -1;
                pcmcia_write_max = -1;
            }
        }
    }
    if (pcmcia_write_min < 0 || pcmcia_write_min > addr)
        pcmcia_write_min = addr;
    if (pcmcia_write_max < 0 || pcmcia_write_max < addr)
        pcmcia_write_max = addr;
}

static int freepcmcia(int reset)
{
    if (pcmcia_sram)
    {
        checkflush(-1);
        if (reset)
        {
            hdf_hd_close(pcmcia_sram);
            free(pcmcia_sram);
            pcmcia_sram = nullptr;
        }
        else
        {
            pcmcia_sram->hfd.drive_empty = 1;
        }
    }
    if (pcmcia_card)
        gayle_cs_change(GAYLE_CS_CCDET, 0);
    pcmcia_card = 0;

    free(pcmcia_common);
    free(pcmcia_attrs);
    pcmcia_common = nullptr;
    pcmcia_attrs = nullptr;
    pcmcia_common_size = 0;
    pcmcia_attrs_size = 0;

    gayle_cfg = 0;
    gayle_cs = 0;
    return 1;
}

static int initpcmcia(const TCHAR* path, int readonly, int reset)
{
    if (g_curr_conf.cs_pcmcia == 0)
        return 0;
    freepcmcia(reset);
    if (!pcmcia_sram)
        pcmcia_sram = xcalloc(struct hd_hardfiledata, 1);
    if (!pcmcia_sram->hfd.handle_valid)
        reset = 1;
    if (reset)
    {
        if (path)
            hdf_hd_open(pcmcia_sram, path, 512, nullptr, 0, 0, 0, 0);
    }
    else
    {
        pcmcia_sram->hfd.drive_empty = 0;
    }
    if (pcmcia_sram->hfd.readonly)
        readonly = 1;
    pcmcia_common_size = 0;
    pcmcia_readonly = readonly;
    pcmcia_attrs_size = 256;
    pcmcia_attrs = xcalloc(byte, pcmcia_attrs_size);
    if (!pcmcia_sram->hfd.drive_empty)
    {
        pcmcia_common_size = pcmcia_sram->hfd.virtsize;
        if (pcmcia_sram->hfd.virtsize > 4 * 1024 * 1024)
        {
            Logger::Write(L"PCMCIA SRAM: too large device, %d bytes\n", pcmcia_sram->hfd.virtsize);
            pcmcia_common_size = 4 * 1024 * 1024;
        }
        pcmcia_common = xcalloc(byte, pcmcia_common_size);
        Logger::Write(L"PCMCIA SRAM: '%s' open, size=%d\n", path, pcmcia_common_size);
        hdf_read(&pcmcia_sram->hfd, pcmcia_common, 0, pcmcia_common_size);
        pcmcia_card = 1;
        initsramattr(pcmcia_common_size, readonly);
        if (!(gayle_cs & GAYLE_CS_DIS))
        {
            gayle_map_pcmcia();
            card_trigger(1);
        }
    }
    pcmcia_write_min = -1;
    pcmcia_write_max = -1;
    return 1;
}

static uint gayle_common_read(uae_ptr addr)
{
    byte v = 0;
    if (Logging::GAYLE_PCMCIA_LOG > 2)
        Logger::Write(L"PCMCIA COMMON R: %x %x\n", addr, M68K_GETPC);
    if (!pcmcia_common_size)
        return 0;
    addr -= PCMCIA_COMMON_START & (PCMCIA_COMMON_SIZE - 1);
    addr &= PCMCIA_COMMON_SIZE - 1;
    if (addr < pcmcia_common_size)
        v = pcmcia_common[addr];
    return v;
}
static void gayle_common_write(uae_ptr addr, uint v)
{
    if (Logging::GAYLE_PCMCIA_LOG > 2)
        Logger::Write(L"PCMCIA COMMON W: %x=%x %x\n", addr, v, M68K_GETPC);
    if (!pcmcia_common_size)
        return;
    if (pcmcia_readonly)
        return;
    addr -= PCMCIA_COMMON_START & (PCMCIA_COMMON_SIZE - 1);
    addr &= PCMCIA_COMMON_SIZE - 1;
    if (addr < pcmcia_common_size)
    {
        if (pcmcia_common[addr] != v)
        {
            checkflush(addr);
            pcmcia_common[addr] = v;
        }
    }
}

static uint REGPARAM3 gayle_common_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle_common_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle_common_bget(uae_ptr) REGPARAM;
static void REGPARAM3 gayle_common_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle_common_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle_common_bput(uae_ptr, uint) REGPARAM;

static int REGPARAM2 gayle_common_check(uae_ptr addr, uint size)
{
    if (!pcmcia_common_size)
        return 0;
    addr -= PCMCIA_COMMON_START & (PCMCIA_COMMON_SIZE - 1);
    addr &= PCMCIA_COMMON_SIZE - 1;
    return (addr + size) <= PCMCIA_COMMON_SIZE;
}

static byte* REGPARAM2 gayle_common_xlate(uae_ptr addr)
{
    addr -= PCMCIA_COMMON_START & (PCMCIA_COMMON_SIZE - 1);
    addr &= PCMCIA_COMMON_SIZE - 1;
    return pcmcia_common + addr;
}

static addrbank gayle_common_bank = {
    gayle_common_lget, gayle_common_wget, gayle_common_bget,
    gayle_common_lput, gayle_common_wput, gayle_common_bput,
    gayle_common_xlate, gayle_common_check, nullptr, L"Gayle PCMCIA Common",
    gayle_common_lget, gayle_common_wget, ABFLAG_RAM | ABFLAG_SAFE
};

static uint REGPARAM3 gayle_attr_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle_attr_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 gayle_attr_bget(uae_ptr) REGPARAM;
static void REGPARAM3 gayle_attr_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle_attr_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 gayle_attr_bput(uae_ptr, uint) REGPARAM;

addrbank gayle_attr_bank = {
    gayle_attr_lget, gayle_attr_wget, gayle_attr_bget,
    gayle_attr_lput, gayle_attr_wput, gayle_attr_bput,
    default_xlate, default_check, nullptr, L"Gayle PCMCIA Attribute/Misc",
    dummy_lgeti, dummy_wgeti, ABFLAG_IO | ABFLAG_SAFE
};

static uint REGPARAM2 gayle_attr_lget(uae_ptr addr)
{
    uint v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = gayle_attr_wget(addr) << 16;
    v |= gayle_attr_wget(addr + 2);
    return v;
}
static uint REGPARAM2 gayle_attr_wget(uae_ptr addr)
{
    ushort v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = gayle_attr_bget(addr) << 8;
    v |= gayle_attr_bget(addr + 1);
    return v;
}
static uint REGPARAM2 gayle_attr_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return gayle_attr_read(addr);
}
static void REGPARAM2 gayle_attr_lput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle_attr_wput(addr, value >> 16);
    gayle_attr_wput(addr + 2, value & 0xffff);
}
static void REGPARAM2 gayle_attr_wput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle_attr_bput(addr, value >> 8);
    gayle_attr_bput(addr + 1, value & 0xff);
}
static void REGPARAM2 gayle_attr_bput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle_attr_write(addr, value);
}

static uint REGPARAM2 gayle_common_lget(uae_ptr addr)
{
    uint v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = gayle_common_wget(addr) << 16;
    v |= gayle_common_wget(addr + 2);
    return v;
}
static uint REGPARAM2 gayle_common_wget(uae_ptr addr)
{
    ushort v;
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    v = gayle_common_bget(addr) << 8;
    v |= gayle_common_bget(addr + 1);
    return v;
}
static uint REGPARAM2 gayle_common_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return gayle_common_read(addr);
}
static void REGPARAM2 gayle_common_lput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle_common_wput(addr, value >> 16);
    gayle_common_wput(addr + 2, value & 0xffff);
}
static void REGPARAM2 gayle_common_wput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle_common_bput(addr, value >> 8);
    gayle_common_bput(addr + 1, value & 0xff);
}
static void REGPARAM2 gayle_common_bput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    gayle_common_write(addr, value);
}

void gayle_map_pcmcia()
{
    if (g_curr_conf.cs_pcmcia == 0)
        return;
    if (pcmcia_card == 0 || (gayle_cs & GAYLE_CS_DIS))
    {
        map_banks(&dummy_bank, 0xa0, 8, 0);
        if (/*g_curr_conf.chipmem_size <= 4 * 1024 * 1024 &&*/ g_curr_conf.fastmem_size <= 4 * 1024 * 1024)
            map_banks(&dummy_bank, PCMCIA_COMMON_START >> 16, PCMCIA_COMMON_SIZE >> 16, 0);
    }
    else
    {
        map_banks(&gayle_attr_bank, 0xa0, 8, 0);
        if (/*g_curr_conf.chipmem_size <= 4 * 1024 * 1024 &&*/ g_curr_conf.fastmem_size <= 4 * 1024 * 1024)
            map_banks(&gayle_common_bank, PCMCIA_COMMON_START >> 16, PCMCIA_COMMON_SIZE >> 16, 0);
    }
}

static int rl(byte* p)
{
    return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
}

void gayle_free()
{
    ide = nullptr;
    for (int i = 0; i < 4; i++)
    {
        delete idedrive[i];
        idedrive[i] = nullptr;
    }
}

void gayle_free_units()
{
    int i;

    for (i = 0; i < 4; i++)
    {
        ide_hdf* ide = idedrive[i];
        if (ide != nullptr)
        {
            hdf_hd_close(&ide->hdhfd);
            ide->clear();
        }
    }
    freepcmcia(1);
}

static void alloc_ide_mem(ide_hdf** ide, int max)
{
    int i;
    for (i = 0; i < max; i++)
    {
        if (ide[i] == nullptr)
            ide[i] = new ide_hdf();
    }
}

#if 0
// #include "zfile.h"
static void dumphdf(struct hardfiledata* hfd)
{
    int i;
    byte buf[512];
    int off;
    struct zfile* zf;

    zf = zfile_fopen("c:\\d\\tmp.dmp", "wb");
    off = 0;
    for (i = 0; i < 128; i++)
    {
        hdf_read(hfd, buf, off, 512);
        zfile_fwrite(buf, 1, 512, zf);
        off += 512;
    }
    zfile_fclose(zf);
}
#endif

int gayle_add_ide_unit(int ch, const TCHAR* path, int blocksize, const TCHAR* devname, int sectors,
    int surfaces, int reserved, int bootpri)
{
    struct ide_hdf* ide;

    if (ch >= 4)
        return -1;
    alloc_ide_mem(idedrive, 4);
    ide = idedrive[ch];
    if (!hdf_hd_open(&ide->hdhfd, path, blocksize, devname, sectors, surfaces, reserved, bootpri))
        return -1;
    ide->blocksize = blocksize;
    ide->lba48 = ide->hdhfd.size >= 128 * (ulong64)0x40000000 ? 1 : 0;
    Logger::Write(L"GAYLE_IDE%d '%s', CHS=%d,%d,%d. %uM. LBA48=%d\n",
        ch, path, ide->hdhfd.cyls, ide->hdhfd.heads, ide->hdhfd.secspertrack, (int)(ide->hdhfd.size / (1024 * 1024)), ide->lba48);
    ide->status = 0;
    ide->data_offset = 0;
    ide->data_size = 0;
    ide->type = IDE_GAYLE;
    // dumphdf (&ide->hdhfd.hfd);
    return 1;
}

int gayle_add_pcmcia_sram_unit(const TCHAR* path, int readonly)
{
    return initpcmcia(path, readonly, 1);
}

int gayle_modify_pcmcia_sram_unit(const TCHAR* path, int readonly, int insert)
{
    if (insert)
        return initpcmcia(path, readonly, pcmcia_sram ? 0 : 1);
    else
        return freepcmcia(0);
}

static void initide()
{
    int i;

    alloc_ide_mem(idedrive, 4);
    if (isrestore())
        return;
    ide_error = 1;
    ide_sector = ide_nsector = 1;
    ide_select = 0;
    ide_lcyl = ide_hcyl = ide_devcon = ide_feat = 0;
    ide_drv = 0;
    ide_splitter = 0;
    if (idedrive[2]->hdhfd.size)
    {
        ide_splitter = 1;
        Logger::Write(L"IDE splitter enabled\n");
    }
    for (i = 0; i < 4; i++)
        idedrive[i]->num = i;
    gayle_irq = gayle_int = 0;
}

void gayle_reset(int hardreset)
{
    static TCHAR bankname[100];

    initide();
    if (hardreset)
    {
        ramsey_config = 0;
        gary_coldboot = 1;
        gary_timeout = 0;
        gary_toenb = 0;
    }
    _tcscpy(bankname, L"Gayle (low)");
    if (g_curr_conf.cs_ide == IDE_A4000)
        _tcscpy(bankname, L"A4000 IDE");
    if (g_curr_conf.cs_mbdmac == 2)
    {
        _tcscat(bankname, L" + NCR53C710 SCSI");
        ncr_reset();
    }
    gayle_bank.name = bankname;
}

byte* restore_gayle(byte* src)
{
    g_changed_conf.cs_ide = restore_u8();
    gayle_int = restore_u8();
    gayle_irq = restore_u8();
    gayle_cs = restore_u8();
    gayle_cs_mask = restore_u8();
    gayle_cfg = restore_u8();
    ide_error = 0;
    return src;
}

byte* save_gayle(int* len, byte* dstptr)
{
    byte* dstbak, * dst;

    if (g_curr_conf.cs_ide <= 0)
        return nullptr;
    if (dstptr)
        dstbak = dst = dstptr;
    else
        dstbak = dst = xmalloc(byte, 1000);
    save_u8(g_curr_conf.cs_ide);
    save_u8(gayle_int);
    save_u8(gayle_irq);
    save_u8(gayle_cs);
    save_u8(gayle_cs_mask);
    save_u8(gayle_cfg);
    *len = dst - dstbak;
    return dstbak;
}

byte* save_ide(int num, int* len, byte* dstptr)
{
    byte* dstbak, * dst;
    struct ide_hdf* ide;

    if (idedrive[num] == nullptr)
        return nullptr;
    if (g_curr_conf.cs_ide <= 0)
        return nullptr;
    ide = idedrive[num];
    if (ide->hdhfd.size == 0)
        return nullptr;
    if (dstptr)
        dstbak = dst = dstptr;
    else
        dstbak = dst = xmalloc(byte, 1000);
    save_u32(num);
    save_u64(ide->hdhfd.size);
    save_string(ide->hdhfd.path);
    save_u32(ide->hdhfd.hfd.blocksize);
    save_u32(ide->hdhfd.hfd.readonly);
    save_u8(ide->multiple_mode);
    save_u32(ide->hdhfd.cyls);
    save_u32(ide->hdhfd.heads);
    save_u32(ide->hdhfd.secspertrack);
    save_u8(ide_select);
    save_u8(ide_nsector);
    save_u8(ide_nsector2);
    save_u8(ide_sector);
    save_u8(ide_sector2);
    save_u8(ide_lcyl);
    save_u8(ide_lcyl2);
    save_u8(ide_hcyl);
    save_u8(ide_hcyl2);
    save_u8(ide_feat);
    save_u8(ide_feat2);
    save_u8(ide_error);
    save_u8(ide_devcon);
    save_u64(ide->hdhfd.hfd.virtual_size);
    save_u32(ide->hdhfd.hfd.secspertrack);
    save_u32(ide->hdhfd.hfd.heads);
    save_u32(ide->hdhfd.hfd.reservedblocks);
    save_u32(ide->hdhfd.bootpri);
    *len = dst - dstbak;
    return dstbak;
}

byte* restore_ide(byte* src)
{
    int num, readonly, blocksize;
    ulong64 size;
    TCHAR* path;
    struct ide_hdf* ide;

    alloc_ide_mem(idedrive, 4);
    num = restore_u32();
    ide = idedrive[num];
    size = restore_u64();
    path = restore_string();
    blocksize = restore_u32();
    readonly = restore_u32();
    ide->multiple_mode = restore_u8();
    ide->hdhfd.cyls = restore_u32();
    ide->hdhfd.heads = restore_u32();
    ide->hdhfd.secspertrack = restore_u32();
    ide_select = restore_u8();
    ide_nsector = restore_u8();
    ide_sector = restore_u8();
    ide_lcyl = restore_u8();
    ide_hcyl = restore_u8();
    ide_feat = restore_u8();
    ide_nsector2 = restore_u8();
    ide_sector2 = restore_u8();
    ide_lcyl2 = restore_u8();
    ide_hcyl2 = restore_u8();
    ide_feat2 = restore_u8();
    ide_error = restore_u8();
    ide_devcon = restore_u8();
    ide->hdhfd.hfd.virtual_size = restore_u64();
    ide->hdhfd.hfd.secspertrack = restore_u32();
    ide->hdhfd.hfd.heads = restore_u32();
    ide->hdhfd.hfd.reservedblocks = restore_u32();
    ide->hdhfd.bootpri = restore_u32();
    if (ide->hdhfd.hfd.virtual_size)
        gayle_add_ide_unit(num, path, blocksize, ide->hdhfd.hfd.device_name,
            ide->hdhfd.hfd.secspertrack, ide->hdhfd.hfd.heads, ide->hdhfd.hfd.reservedblocks,
            ide->hdhfd.bootpri);
    else
        gayle_add_ide_unit(num, path, blocksize, 0, 0, 0, 0, 0);
    free(path);
    return src;
}